package com.jxmcloud.business.cut.exception;

import com.google.common.base.Joiner;
import com.xlcloud.platform.api.exception.BusinessException;
import com.xlcloud.platform.util.PublicUtil;
import com.xlcloud.platform.util.RUtil;
import org.apache.catalina.connector.ClientAbortException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;

/**
 * 全局异常铺获
 * @author hujun
 */
@RestControllerAdvice
public class GlobalExceptionAdvice {

	private final Logger logger = LoggerFactory.getLogger(GlobalExceptionAdvice.class);

	/**
	 * 系统异常全局铺获
	 * @author hujun
	 **/
	@ExceptionHandler(Exception.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	@ResponseBody
	public String exception(HttpServletResponse response,Exception e) {
//		logger.error("全局异常信息: {}", e);
		return RUtil.appFail(response, "系统繁忙");
	}

	/**
	 * 业务异常全局铺获
	 * @author hujun
	 **/
	@ExceptionHandler(BusinessException.class)
	@ResponseStatus(HttpStatus.OK)
	@ResponseBody
	public String businessException(com.xlcloud.platform.api.exception.BusinessException e, HttpServletResponse response) {
//		logger.error("全局异常信息: {}",e);
		return RUtil.appFail(response,e.getMessage());
	}


	/**
	 * 请求参数类型异常捕获
	 * 2019-03-14 10:52
	 *  @author 许愿
	 */
	@ExceptionHandler({TypeMismatchException.class})
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public String requestTypeMismatch(TypeMismatchException e, HttpServletResponse response, HttpServletRequest request){
        String url="http://" + request.getServerName() //服务器地址
                + ":"
                + request.getServerPort()           //端口号
                + request.getRequestURI();
        logger.error("参数类型错误,接口地址"+url);
        Enumeration<String> enu=request.getParameterNames();
        while(enu.hasMoreElements()){
            String paraName=(String)enu.nextElement();
            logger.error(paraName+": "+request.getParameter(paraName));
        }

        return RUtil.appFail(response,"参数类型错误");
	}
	/**
	 * 参数类型缺失捕获
	 * 2019-03-14 10:52
	 *  @author 许愿
	 */
	@ExceptionHandler({MissingServletRequestParameterException.class})
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public String requestMissingServletRequest(MissingServletRequestParameterException e,HttpServletResponse response,HttpServletRequest request){
        String url="http://" + request.getServerName() //服务器地址
                + ":"
                + request.getServerPort()           //端口号
                + request.getRequestURI();
	    logger.error("缺少必要参数,接口地址"+url);
		Enumeration<String> enu=request.getParameterNames();
		while(enu.hasMoreElements()){
			String paraName=(String)enu.nextElement();
			logger.error(paraName+": "+request.getParameter(paraName));
		}
		return RUtil.appFail(response,"缺少必要参数");
	}



	private String getErrMsg(BindingResult bindingResult) {
		List<String> tips = new ArrayList<>();
		List<? extends ObjectError> errors = bindingResult.getFieldErrors();
		if(PublicUtil.isEmpty(errors)){
			//获取不到FieldErrors的时候获取AllErrors
			errors = bindingResult.getAllErrors();
		}
		for (ObjectError error : errors) {
			tips.add(error.getDefaultMessage());
		}
		String msg = Joiner.on("；").join(tips);
		logger.error("valid error:{}", msg);
		return msg;
	}

	/**
	 * bean校验未通过异常
	 *
	 * @see javax.validation.Valid
	 * @see org.springframework.validation.Validator
	 * @see org.springframework.validation.DataBinder
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public String paramNotValidHandler(MethodArgumentNotValidException e, HttpServletResponse res) {
		BindingResult bindingResult = e.getBindingResult();
		return RUtil.appFail(res, getErrMsg(bindingResult));
	}

	@ExceptionHandler(BindException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public String paramBindErrorHandler(BindException e, HttpServletResponse res) {
		BindingResult bindingResult = e.getBindingResult();
		return RUtil.appFail(res, getErrMsg(bindingResult));
	}
	/**
	 * 捕获 ClientAbortException 异常, 不做任何处理, 防止出现大量堆栈日志输出, 此异常不影响功能. 一般为客户端主动断联  或者服务端超时 nginx proxy_read_timeout 过于短了
	 */
	@ExceptionHandler(ClientAbortException.class)
	public void handleLockException(ClientAbortException exception, HttpServletRequest request) {
		final String message = "ClientAbortException generated by request {} {} from remote address {} with X-FORWARDED-FOR {}";
		final String headerXFF = request.getHeader("X-FORWARDED-FOR");
		logger.warn(message, request.getMethod(), request.getRequestURL(), request.getRemoteAddr(), headerXFF);
	}

	@ExceptionHandler(ConstraintViolationException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public String validError(ConstraintViolationException e, HttpServletResponse res) {
		// 单个参数异常
		Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
		// 打印数据
		StringBuilder builder = new StringBuilder();
		violations.forEach(x -> builder.append(x.getMessage()).append(";"));
		return RUtil.appFail(res, builder.toString());
	}

}
